<template>
  <div>
    <div id="gid_tc" style="float: left">
      <div id="gid"></div>
      <div class="mengceng"></div>
    </div>
    <div class="svg-set-box clearfix">
      <div class="ctwh-dibmr">
        <span>显示范围：</span>
        <a
          :key="index"
          v-for="(m, index) in pageSizeList"
          @click="setMatchSize(m, this)"
          href="javascript:void(0)"
          :class="[m.isActive ? 'sd-active' : '', 'ss-d sd' + (index + 1)]"
        >
        </a>
      </div>
      <div class="ctwh-dibmr" style="float: right">
        <ul class="toolbar">
          <li>
            <a href="javascript:;" @click="zoomIn"
              ><span><i class="el-icon-zoom-in"></i>放大</span></a
            >
          </li>
          <li>
            <a href="javascript:;" @click="zoomOut"
              ><span><i class="el-icon-zoom-out"></i>缩小</span></a
            >
          </li>
          <li>
            <a href="javascript:;" @click="refresh"
              ><span><i class="el-icon-refresh-right"></i>还原</span></a
            >
          </li>
          <li>
            <a
              v-if="!isFullscreen"
              id="fullscreenbtn"
              href="javascript:;"
              @click="showFull"
            >
              <span><i class="el-icon-full-screen"></i>全屏</span>
            </a>
            <a v-else id="fullscreenbtn" href="javascript:;" @click="exitFullScreen">
              <span><i class="el-icon-full-screen"></i>退出全屏</span>
            </a>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>
<script>
import axios from "axios";
import * as d3 from "d3";
import $ from "jquery";

export default {
  props: ["pid"],
  data() {
    return {
      theme: 0,
      loading: false,
      width: 1000,
      height: 800,
      gcontainer: {},
      svg: {},
      zoom: null,
      arrowMarker: {},
      simulation: {},
      isFullscreen: false,
      qaGraphNode: {},
      qaButtonGroup: {},
      qaGraphNodeText: {},
      qaGraphLink: {},
      qaGraphLinkText: {},
      graph: {
        nodes: [],
        links: [],
      },
      defaultR: 30,
      colorList: ["#ff8373", "#f9c62c", "#a5ca34", "#6fce7a", "#70d3bd", "#ea91b0"],
      pageSizeList: [
        { size: 100, isActive: false },
        { size: 300, isActive: false },
        { size: 500, isActive: true },
        { size: 1000, isActive: false },
      ],
      toolbarData: [
        { name: "编辑", value: 1, code: "edit" },
        { name: "展开", value: 1, code: "more" },
        { name: "追加", value: 1, code: "append" },
        { name: "连线", value: 1, code: "link" },
        { name: "删除", value: 1, code: "delete" },
      ],
      selectUuid: 0,
      nodeRecordList: [],
    };
  },
  components: {},
  mounted() {
    this.initGraphContainer();
    this.addMaker();
    this.initGraph();
  },
  created() {},
  watch: {},
  methods: {
    initGraphContainer() {
      this.gcontainer = d3.select("#gid");
      if (this.isFullscreen) {
        this.width = window.screen.width;
        this.height = window.screen.height;
      } else {
        this.width = $("#" + this.pid).width();
        this.height = $("#" + this.pid).height();
      }
      this.svg = this.gcontainer.append("svg");
      var sWidth = this.width;
      var sHeight = this.height;
      this.svg.attr("width", sWidth);
      this.svg.attr("height", sHeight);
      // this.svg.attr("viewBox", "0 0 " + sWidth / 2 + " " + sHeight / 2);
      this.svg.attr("id", "svg_idx");
      this.svg.attr("preserveAspectRatio", "xMidYMidmeet");
      this.simulation = d3
        .forceSimulation()
        .force("charge", d3.forceManyBody().strength(-1500))
        .force(
          "link",
          d3
            .forceLink()
            .distance(60)
            .id(function (d) {
              return d.uuid;
            })
        )
        .force("collide", d3.forceCollide().strength(-30))
        .force("center", d3.forceCenter(this.width / 2, this.height / 2));
      this.qaGraphLink = this.svg.append("g").attr("class", "line");
      this.qaGraphLinkText = this.svg.append("g").attr("class", "lineText");
      this.qaGraphNode = this.svg.append("g").attr("class", "node");
      this.qaGraphNodeText = this.svg.append("g").attr("class", "nodeText");
      this.nodeButtonGroup = this.svg.append("g").attr("class", "nodeButton");
      this.svg.on(
        "click",
        function () {
          d3.selectAll(".buttongroup").classed("notshow", true);
        },
        false
      );
    },
    initGraph() {
      var _this = this;
      axios.get("/static/kgData.json", {}).then(function (response) {
        var data = response.data;
        _this.graph.nodes = data.node;
        _this.graph.links = data.relationship;
        _this.updateGraph();
      });
    },
    addMaker() {
      var arrowMarker = this.svg
        .append("marker")
        .attr("id", "arrow")
        .attr("markerUnits", "strokeWidth")
        .attr("markerWidth", "12") //
        .attr("markerHeight", "12")
        .attr("viewBox", "0 0 12 12")
        .attr("refX", "30")
        .attr("refY", "6")
        .attr("orient", "auto");
      var arrowPath = "M2,2 L10,6 L2,10 L6,6 L2,2"; // 定义箭头形状
      arrowMarker.append("path").attr("d", arrowPath).attr("fill", "#ccc");
    },
    openNode() {
      var _this = this;
      var noddd = [
        {
          flag: "1",
          code: "27301111",
          parentCode: "273",
          grade: "2",
          name: "儒家2",
          uuid: "4617858011",
        },
        {
          code: "273012222",
          flag: "1",
          parentCode: "273",
          grade: "3",
          name: "故事轶闻2",
          uuid: "2636501111",
        },
      ];
      var newships = [
        {
          sourceId: "273",
          targetId: "2636501111",
          name: "",
          targetcode: "2730107",
          uuid: "91804213",
          sourcecode: "27301",
        },
        {
          sourceId: "273",
          targetId: "4617858011",
          name: "",
          targetcode: "273010723",
          uuid: "91804389",
          sourcecode: "2730107",
        },
      ];
      _this.graph.nodes = _this.graph.nodes.concat(noddd);
      _this.graph.links = _this.graph.links.concat(newships);
      _this.updateGraph();
    },
    drawNode(nodes) {
      var _this = this;
      var node = this.qaGraphNode.selectAll("circle").data(nodes, function (d) {
        return d.uuid;
      });
      node.exit().remove();
      var nodeEnter = node.enter().append("circle");
      nodeEnter.on("click", function (d) {
        console.log("触发单击");
        _this.selectUuid = d.uuid;
        var out_buttongroup_id = ".out_buttongroup_" + d.uuid;
        var selectItem = d3.select(out_buttongroup_id)._groups[0][0];
        if (selectItem.classList.contains("notshow")) {
          _this.svg.selectAll(".buttongroup").classed("notshow", true);
          d3.select(out_buttongroup_id).classed("notshow", false);
        } else {
          d3.select(out_buttongroup_id).classed("notshow", true);
        }
        event.stopPropagation();
      });
      nodeEnter.on("dblclick", function (d) {
        console.log("触发双击:" + d);
        event.preventDefault();
      });
      nodeEnter.on("mouseenter", function () {
        console.log("鼠标移入");
        d3.select(this).style("stroke-width", "6");
      });
      nodeEnter.on("mouseleave", function () {
        console.log("鼠标移出");
        d3.select(this).style("stroke-width", 2);
        //todo其他节点和连线一并显示
        d3.select(".node").style("fill-opacity", 1);
        d3.select(".nodeText").style("fill-opacity", 1);
        d3.selectAll(".line").style("stroke-opacity", 1);
        d3.selectAll(".lineText").style("fill-opacity", 1);
      });
      nodeEnter.on("mouseover", function (d) {
        //todo鼠标放上去只显示相关节点，其他节点和连线隐藏
        d3.selectAll(".node").style("fill-opacity", 0.1);
        var relvantNodeIds = [];
        var relvantNodes = _this.graph.links.filter(function (n) {
          return n.sourceId == d.uuid || n.targetId == d.uuid;
        });
        relvantNodes.forEach(function (item) {
          relvantNodeIds.push(item.sourceId);
          relvantNodeIds.push(item.targetId);
        });
        //显示相关的节点
        _this.qaGraphNode.selectAll("circle").style("fill-opacity", function (c) {
          if (relvantNodeIds.indexOf(c.uuid) > -1) {
            return 1.0;
          }
        });
        //透明所有节点文字
        d3.selectAll(".nodeText").style("fill-opacity", 0.1);
        //显示相关的节点文字
        _this.qaGraphNodeText.selectAll("text").style("fill-opacity", function (c) {
          if (relvantNodeIds.indexOf(c.uuid) > -1) {
            return 1.0;
          }
        });
        //透明所有连线
        d3.selectAll(".line").style("stroke-opacity", 0.1);
        //显示相关的连线
        _this.qaGraphLink.selectAll("line").style("stroke-opacity", function (c) {
          if (c.lk.targetId === d.uuid) {
            console.log(c);
            return 1.0;
          }
        });
        //透明所有连线文字
        d3.selectAll(".lineText").style("fill-opacity", 0.1);
        //显示相关的连线文字
        _this.qaGraphLinkText.selectAll(".lineText").style("fill-opacity", function (c) {
          if (c.lk.targetId === d.uuid) {
            return 1.0;
          }
        });
      });
      nodeEnter.call(
        d3
          .drag()
          .on("start", _this.dragStarted)
          .on("drag", _this.dragged)
          .on("end", _this.dragEnded)
      );
      node = nodeEnter.merge(node).text(function (d) {
        return d.name;
      });
      node.style("stroke", function (d) {
        if (d.color) {
          return d.color;
        }
        return "#A4ED6C";
      });
      node.style("stroke-opacity", 0.6);
      node.attr("r", function (d) {
        if (d.r) {
          return d.r;
        }
        return d.index === 0 ? 28 : 20;
      });
      node.attr("fill", function (d, i) {
        //创建圆形图像
        if (d.imgsrc) {
          var img_w = 77,
            img_h = 80;
          var defs = d3.selectAll("svg >defs");
          var catpattern = defs
            .append("pattern")
            .attr("id", "catpattern" + i)
            .attr("height", 1)
            .attr("width", 1);
          catpattern
            .append("image")
            .attr("x", -(img_w / 2 - d.r))
            .attr("y", -(img_h / 2 - d.r))
            .attr("width", img_w)
            .attr("height", img_h)
            .attr("xlink:href", d.imgsrc);
          return "url(#catpattern" + i + ")";
        } else {
          if (d.cur === "1") {
            return _this.colorList[0];
          } else {
            return _this.colorList[2];
          }
        }
      });
      node
        .append("title") // 为每个节点设置title
        .text(function (d) {
          if (d.name) {
            return d.name;
          }
          return "";
        });
      return node;
    },
    drawNodeText(nodes) {
      var _this = this;
      var nodeText = this.qaGraphNodeText.selectAll("text").data(nodes, function (d) {
        return d.uuid;
      });
      nodeText.exit().remove();
      var nodeTextEnter = nodeText.enter().append("text");
      nodeTextEnter.call(
        d3
          .drag()
          .on("start", _this.dragStarted)
          .on("drag", _this.dragged)
          .on("end", _this.dragEnded)
      );
      nodeText = nodeTextEnter.merge(nodeText).text(function (d) {
        return d.name;
      });
      nodeText
        .style("fill", function () {
          return "#333";
        })
        .attr("class", "nodeText")
        .attr("dy", "3.6em")
        .attr("font-family", "宋体")
        .attr("font-size", 16)
        .attr("text-anchor", "middle")
        .text(function (d) {
          return d.name;
        });
      nodeText
        .append("title") // 为每个节点设置title
        .text(function (d) {
          if (d.name) {
            return d.name;
          }
          return "";
        });
      return nodeText;
    },
    drawLink(links) {
      var _this = this;
      var link = this.qaGraphLink.selectAll("line").data(links, function (d) {
        return d.uuid;
      });
      link.exit().remove();
      var linkEnter = link
        .enter()
        .append("line")
        .attr("class", "link")
        .attr("stroke-width", 1)
        .attr("stroke", function () {
          return _this.colorList[2];
        })
        .attr("fill", function () {
          return _this.colorList[2];
        })
        .attr("marker-end", "url(#arrow)"); // 箭头
      link = linkEnter.merge(link);
      return link;
    },
    drawLinkText(links) {
      var _this = this;
      var linktext = _this.qaGraphLinkText.selectAll("text").data(links, function (d) {
        return d.uuid;
      });
      linktext.exit().remove();
      var linkTextEnter = linktext
        .enter()
        .append("text")
        .attr("class", "lineText")
        .style("fill", "#875034")
        .style("font-size", "16px")
        .text(function (d) {
          return d.lk.name;
        });
      linktext = linkTextEnter.merge(linktext).text(function (d) {
        return d.lk.name;
      });
      return linktext;
    },
    drawButtonGroup(nodes) {
      var _this = this;
      d3.selectAll(".nodeButton >g").remove();
      var nodeButton = _this.nodeButtonGroup
        .selectAll(".nodeButton")
        .data(nodes, function (d) {
          return d.uuid;
        });
      nodeButton.exit().remove();
      var nodeButtonEnter = nodeButton
        .enter()
        .append("use") //  为每个节点组添加一个 use 子元素
        .attr("r", function (d) {
          if (!d.r) {
            return _this.defaultR;
          }
          return d.r;
        })
        .attr("uuid", function (d) {
          return d.uuid;
        })
        .attr("xlink:href", function (d) {
          if (!d.r) {
            return "#out_circle_" + _this.defaultR;
          }
          return "#out_circle_" + d.r;
        }) //  指定 use 引用的内容
        .attr("class", function (d) {
          return "buttongroup out_buttongroup_" + d.uuid;
        })
        .classed("notshow", true);
      nodeButton = nodeButtonEnter.merge(nodeButton);
      return nodeButton;
    },
    drawToolButton() {
      var _this = this;
      //先删除所有为节点自定义的按钮组
      d3.selectAll("svg >defs").remove();
      var nodes = _this.graph.nodes;
      var pie = d3.pie().value(function (d) {
        return d.value; //处理数据，指定value作为计算比例的字段
      });
      var piedata = pie(_this.toolbarData);
      var nodeButtonGroup = this.svg.append("defs");
      var nodeRArr = [];
      nodes.forEach(function (m) {
        if (!m.r) {
          m.r = _this.defaultR;
        }
        //按半径分别定义每种按钮组的图标
        if (nodeRArr.indexOf(m.r) == -1) {
          nodeRArr.push(m.r);
          var nbtng = nodeButtonGroup.append("g").attr("id", "out_circle_" + m.r); //为每一个节点定制一个按钮组，在画按钮组的时候为其指定该id
          var buttonGroupEnter = nbtng
            .selectAll(".buttongroup")
            .data(piedata)
            .enter()
            .append("g")
            .attr("class", function (d) {
              return "action_" + d.data.code;
            });
          var arc = d3
            .arc()
            .innerRadius(m.r)
            .outerRadius(m.r + 30);
          buttonGroupEnter
            .append("path")
            .attr("d", function (d) {
              return arc(d);
            })
            .attr("fill", "#E6A23C")
            .style("opacity", 0.6)
            .attr("stroke", "#6CB7ED")
            .attr("stroke-width", 1);
          buttonGroupEnter
            .append("text")
            .attr("transform", function (d) {
              return "translate(" + arc.centroid(d) + ")";
            })
            .attr("text-anchor", "middle")
            .text(function (d) {
              return d.data.name;
            })
            .style("fill", function () {
              return "#fff";
            })
            .attr("font-size", 10);
        }
      });
    },
    bindEventButtonGroup() {
      var _this = this;
      //按钮组事件绑定
      _this.toolbarData.forEach(function (m) {
        var btnClass = ".action_" + m.code;
        _this.svg.selectAll(btnClass).on("click", function (d) {
          console.log(d.data.name + ":" + d.data.code + ":uuid:" + _this.selectUuid);
        });
      });
    },
    formatData() {
      var _this = this;
      var lks = _this.graph.links;
      var nodes = _this.graph.nodes;
      nodes.forEach(function (n) {
        if (n.center === 1 || n.center === "1") {
          n.fx = _this.width / 2;
          n.fy = _this.height / 2;
        }
        if (typeof n.fx === "undefined" || n.fx === "") {
          n.fx = null;
        }
        if (typeof n.fy === "undefined" || n.fy === "") {
          n.fy = null;
        }
      });
      var links = [];
      lks.forEach(function (m) {
        var sourceNode = nodes.filter(function (n) {
          return n.uuid === m.sourceId;
        })[0];
        if (!sourceNode) return;
        var targetNode = nodes.filter(function (n) {
          return n.uuid === m.targetId;
        })[0];
        if (!targetNode) return;
        links.push({ source: sourceNode.uuid, target: targetNode.uuid, lk: m });
      });
      var data = {};
      data.nodes = nodes;
      data.links = links;
      return data;
    },
    updateGraph() {
      var _this = this;
      var data = _this.formatData();
      var nodes = data.nodes;
      var links = data.links;
      //定义按钮组引用的use元素
      _this.drawToolButton(nodes);
      // 更新节点
      var graphNode = _this.drawNode(nodes);
      // 更新节点文字
      var graphNodeText = _this.drawNodeText(nodes);
      // 更新按钮组
      var graphNodeButtonGroup = _this.drawButtonGroup(nodes);
      // 更新连线 links
      var graphLink = _this.drawLink(links);
      // 更新连线文字
      var graphLinkText = _this.drawLinkText(links);
      _this.simulation.nodes(nodes).alphaTarget(0).alphaDecay(0.05).on("tick", ticked);
      function ticked() {
        // 更新连线坐标
        graphLink
          .attr("x1", function (d) {
            addNodeButton;
            return d.source.x;
          })
          .attr("y1", function (d) {
            return d.source.y;
          })
          .attr("x2", function (d) {
            return d.target.x;
          })
          .attr("y2", function (d) {
            return d.target.y;
          });
        // 刷新连接线上的文字位置
        graphLinkText
          .attr("x", function (d) {
            if (!d.source.x || !d.target.x) return 0;
            var x = (parseFloat(d.source.x) + parseFloat(d.target.x)) / 2;
            return x;
          })
          .attr("y", function (d) {
            if (!d.source.y || !d.target.y) return 0;
            var y = (parseFloat(d.source.y) + parseFloat(d.target.y)) / 2;
            return y;
          });
        // 更新节点坐标
        graphNode
          .attr("cx", function (d) {
            return d.x;
          })
          .attr("cy", function (d) {
            return d.y;
          });
        // 更新节点操作按钮组坐标
        graphNodeButtonGroup
          .attr("cx", function (d) {
            return d.x;
          })
          .attr("cy", function (d) {
            return d.y;
          })
          .attr("transform", function (d) {
            return "translate(" + d.x + "," + d.y + ") scale(1)";
          });
        // 更新文字坐标
        graphNodeText
          .attr("x", function (d) {
            return d.x;
          })
          .attr("y", function (d) {
            return d.y;
          });
      }
      _this.simulation.force("link").links(links);
      _this.simulation.force("center", d3.forceCenter(_this.width / 2, _this.height / 2));
      _this.simulation.alpha(1).restart();
      // 鼠标滚轮缩放
      _this.zoom = d3.zoom().scaleExtent([0.1, 4]).on("zoom", _this.zoomed);
      _this.svg.call(_this.zoom);
      // 静止双击缩放
      _this.svg.on("dblclick.zoom", null);
      //为按钮组绑定事件
      _this.bindEventButtonGroup();
    },
    dragStarted(d) {
      if (!d3.event.active) this.simulation.alphaTarget(0.8).restart();
      d.x = d3.event.x;
      d.y = d3.event.y;
      d.fx = d.x;
      d.fy = d.y;
    },
    dragged(d) {
      d.x = d3.event.x;
      d.y = d3.event.y;
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    },
    dragEnded(d) {
      if (!d3.event.active) this.simulation.alphaTarget(0);
      d.x = d3.event.x;
      d.y = d3.event.y;
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    },
    zoomed() {
      d3.selectAll(".node").attr("transform", d3.event.transform);
      d3.selectAll(".nodeText text").attr("transform", d3.event.transform);
      d3.selectAll(".line").attr("transform", d3.event.transform);
      d3.selectAll(".lineText text").attr("transform", d3.event.transform);
      d3.selectAll(".nodeButton").attr("transform", d3.event.transform);
      //_this.svg.selectAll("g").attr("transform", d3.event.transform);
    },
    zoomClick(direction) {
      var self = this;
      var factor = 0.2;
      var targetZoom = 1;
      var extent = self.zoom.scaleExtent();
      targetZoom = 1 + factor * direction;
      if (targetZoom < extent[0] || targetZoom > extent[1]) {
        return false;
      }
      self.zoom.scaleBy(self.svg, targetZoom); // 执行该方法后 会触发zoom事件
    },
    zoomIn() {
      this.zoomClick(1);
    },
    zoomOut() {
      this.zoomClick(-1);
    },
    refresh() {
      this.svg.call(this.zoom.transform, d3.zoomIdentity);
    },
    showFull() {
      this.isFullscreen = !this.isFullscreen;
      var full = document.getElementById("kg_container");
      this.fullScreen(full);
    },
    fullScreen(element) {
      if (element.requestFullscreen) {
        element.requestFullscreen();
      } else if (element.mozRequestFullScreen) {
        element.mozRequestFullScreen();
      } else if (element.webkitRequestFullscreen) {
        element.webkitRequestFullscreen();
      } else if (element.msRequestFullscreen) {
        element.msRequestFullscreen();
      }
    },
    exitFullScreen() {
      this.isFullscreen = !this.isFullscreen;
      if (document.exitFullscreen) {
        document.exitFullscreen();
      } else if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
      } else if (document.webkitExitFullscreen) {
        document.webkitExitFullscreen();
      }
    },
    btnCollapseNode() {},
    btnOpenNode() {},
    close() {},
  },
};
</script>
<style>
.svg-set-box {
  height: 46px;
  line-height: 46px;
  padding-left: 15px;
  color: #7f7f7f;
  /* background: #f7f7f7; */
  position: absolute;
  bottom: 0;
}
.ctwh-dibmr {
  display: inline-block;
}
.ss-d {
  display: inline-block;
  vertical-align: middle;
  margin-right: 10px;
  border-radius: 50%;
  background: #dedede;
}
.sd1 {
  width: 30px;
  height: 30px;
}
.sd2 {
  width: 26px;
  height: 26px;
}
.sd3 {
  width: 20px;
  height: 20px;
}
.sd4 {
  width: 16px;
  height: 16px;
}
.sd-active {
  background: #08aefc !important;
}
.toolbar {
  margin-left: 150px;
  margin-right: 15px;
  line-height: 18px;
}
ul,
li {
  list-style: none;
}
.toolbar li {
  float: left;
  width: 60px;
}
.notshow {
  display: none;
}
.nodeText {
  pointer-events: all;
  cursor: pointer;
  stroke-dasharray: 0 0 0 0;
  stroke-dashoffset: 10;
  transition: all ease 0.1s;
}
.nodeText:hover {
  stroke-dashoffset: 0;
  stroke-dasharray: 100;
}
</style>
